package com.xiyuan.smartutils.template.engine.statement;

import com.xiyuan.smartutils.StrUtils;
import com.xiyuan.smartutils.Validates;
import com.xiyuan.smartutils.template.engine.StatementParser;
import com.xiyuan.smartutils.template.engine.TagType;
import com.xiyuan.smartutils.template.engine.VariableMap;
import com.xiyuan.smartutils.template.engine.exception.StatementException;
import com.xiyuan.smartutils.template.engine.statement._Break.BreakException;
import com.xiyuan.smartutils.template.engine.statement._Continue.ContinueException;

import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.Iterator;

public class _FOR extends Statement {
    private static final long serialVersionUID = 1L;
    private String forSymbol;// 循环点
    private String item_name = "item";
    private String index_name = "item_index";
    
    public _FOR(StatementParser parser) {
        super(parser);
    }
    
    
    private boolean isContinueException(Throwable e, VariableMap map) {
        ContinueException te = null;
        return e instanceof ContinueException && ((te = (ContinueException) e).getForSymbol() == null || te.getForSymbol()
                .equals(this.forSymbol));
    }
    
    private boolean isBreakException(Throwable e, VariableMap map) {
        BreakException te = null;
        return e instanceof ContinueException && ((te = (BreakException) e).getForSymbol() == null || te.getForSymbol()
                .equals(this.forSymbol));
    }
    
    @Override
    protected void build(VariableMap variableMap, PrintWriter out) {
        if (expressions.isEmpty()) {
            throw new StatementException(this.getPath(),
                                         getLineNo(),
                                         getBeginColumn(),
                                         getType() + "缺少数据迭代表达式");
        }
        Object arr = expressions.get(0).execute(variableMap);
        if (arr == null) {
            return;// 无变量，直接返回null
        }
        
        if (!(arr instanceof Iterator) && !(arr instanceof Iterable) && !arr.getClass().isArray()) {
            if (!hasChild()) {return;}
        }
        
        // 创建局部变量空间
        variableMap.newVariableSpace();
        try {
            // 加入循环控制标志
            if (arr.getClass().isArray()) {
                int length = Array.getLength(arr);
                op:
                for (int i = 0; i < length; i++) {
                    try {
                        Object item = Array.get(arr, i);
                        // 设置域变量
                        variableMap.putValue(item_name, item);
                        variableMap.putValue(index_name, i);
                        // 处理与标签内容
                        each(i, item, out, variableMap);
                    }
                    catch (Throwable e) {
                        BreakException be = null;
                        ContinueException ce = null;
                        Throwable ex = e;
                        do {
                            if (isBreakException(ex, variableMap)) {continue op;}
                            else if (isBreakException(ex, variableMap)) break op;
                        }
                        while (((ex) = e.getCause()) != null);
                        
                        if (e instanceof StatementException) {throw e;}
                        String msg = e.getMessage();
                        throw new StatementException(getPath(), getLineNo(), getBeginColumn(), this + " " + msg, e);
                    }
                    finally {// 移除域变量
                        variableMap.removeValue(item_name);
                        variableMap.removeValue(index_name);
                    }
                }
            }
            else {
                Iterator<?> it = null;
                if (arr instanceof Iterable) {it = ((Iterable<?>) arr).iterator();}
                else {it = (Iterator<?>) arr;}
                
                for (int i = 0; it.hasNext(); i++) {
                    
                    try {
                        Object item = it.next();
                        // 设置域变量
                        variableMap.putValue(item_name, item);
                        variableMap.putValue(index_name, i);
                        // 处理与标签内容
                        each(i, item, out, variableMap);
                    }
                    catch (BreakException e) {
                        if (e.getForSymbol() == null || e.getForSymbol().equals(this.forSymbol)) {break;}
                        else {
                            throw e;// 继续跳父及循环
                        }
                    }
                    catch (ContinueException e) {
                        if (e.getForSymbol() == null || e.getForSymbol().equals(this.forSymbol)) {
                            continue;// 继续当前循环
                        }
                        else {
                            throw e;// 继续跳父及循环
                        }
                    }
                    finally {// 移除域变量
                        variableMap.removeValue(item_name);
                        variableMap.removeValue(index_name);
                    }
                }
            }
        }
        finally {// 最后移除变量空间
            variableMap.releaseVariableSpace();
        }
    }
    
    /**
     * 遍历对象
     *
     * @param index
     * @return 返回 0=正常遍历，1=continue, 2=break;
     * @throws Exception
     */
    private void each(int index, Object item, PrintWriter out, VariableMap variableMap) {
        
        Iterator<Statement> iterator = childs.iterator();
        while (iterator.hasNext()) {
            // 循环内容
            Statement stmt = iterator.next();
            stmt.execute(variableMap, out);
        }
    }
    
    @Override
    public TagType getType() {
        return TagType.FOR;
    }
    
    /**
     * 索引备注1两种情况<br>
     * 1)<#for a:b:c>a=索引名称，b=对象名，c=集合<br>
     * 2)<#for a:c> b=对象名，c=集合,索引名称=item_index(系统默认)<br>
     * 3)<#for c> c=集合，索引名称=item_index(系统默认)，对象名=item(系统默认)<br>
     */
    @Override
    public boolean parseTag(StatementParser parser) throws StatementException {
        StringBuilder value = new StringBuilder();
        char ch = 0, quote = 0, prvChar = 0;
        boolean findName = false, space = false;
        int expression_begin_line = 0, expression_begin_column = 0;
        
        while (parser.hasNext()) {
            prvChar = value.length() == 0 ? 0 : ch;
            ch = parser.next();
            if (value.length() == 0 && Validates.isWhitespace(ch)) {
                space = true;
                continue;// 过滤空白
            }
            
            value.append(ch);
            if (!space) {
                return false;// 只是以#include开头而已
            }
            
            if (value.length() == 1) {
                expression_begin_line = parser.getLineNum();
                expression_begin_column = parser.getColumn();
            }
            // 解析数据值
            if (quote == 0 && (ch == _DOUBLE_QUOTE_ || ch == _QUOTE_)) {// 记录 字符串开始
                quote = ch;
                continue;
            }
            
            // 字符串结束
            if (quote != 0 && prvChar != _BACK_SLASH_ && ch == quote) {
                quote = 0;
                continue;
            }
            
            // 判断标签是否结束 ,当前标签为‘>’视为结束
            int endTagIndex = ch == _GATHER_ ? value.length() - 1 : -1;
            if (quote == 0 && endTagIndex != -1) {// 结束
                this.setCompleteTag();// 标签开始就绪
                value.setLength(value.length() - 1);// 表达式不包括结束符
                super.parseExpression(expression_begin_line, expression_begin_column, value);// 解析表达式
                value.setLength(0);// 解析
                break;
            }
            
            // 解析参数名
            if (quote == 0 && ch == _COLON_ && !findName) {
                
                value.setLength(value.length() - 1);
                if (value.length() == 0) {continue;}
                
                String variableName = StrUtils.trim(value.toString());
                boolean errName = false;
                if (variableName.length() > 0 && !Validates.isJavaNameIdentifier(variableName)) {errName = true;}
                
                // 计数错误行列
                for (int j = 0; j < value.length(); j++) {
                    char c = value.charAt(j);
                    if (c == _LF_) {
                        expression_begin_line++;
                        expression_begin_column = 1;
                    }
                    
                    if (c != _LF_ && c != _CR_) {expression_begin_column++;}
                    
                    if (!Validates.isWhitespace(c) && errName) {break;}
                }
                
                if (errName) {
                    throw new StatementException(this.getPath(), expression_begin_line, expression_begin_column,
                                                 StrUtils.formats("%s标签解析时无效属性名，请参见java命名规范。数据值:[%s]",
                                                                  getType(),
                                                                  variableName));
                }
                
                if (!findName) {
                    findName = true;
                    this.item_name = variableName;
                    value.setLength(0);
                    continue;
                }
            }
            
        }
        
        if (!this.isCompleteTag()) {
            throw new StatementException(this.getPath(),
                                         this.getLineNo(),
                                         this.getBeginColumn(),
                                         "文档已结束。" + getType() + "标签缺少‘>’");
        }
        
        if (expressions.isEmpty()) {
            throw new StatementException(this.getPath(),
                                         this.getLineNo(),
                                         this.getBeginColumn(),
                                         "文档已结束。" + getType() + "标签 缺少集合表达式定义");
        }
        
        this.index_name = this.item_name + "_index";
        this.setStatement(this.getStatement() + " " + this.item_name + " : " + this.expressions.get(0) + ">");
        return true;
    }
    
}
